/*
* Copyright 2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <stdio.h>
#include <string.h>
#include "fsl_sd.h"
#include "fsl_debug_console.h"
#include "ff.h"
#include "diskio.h"
#include "fsl_sd_disk.h"
#include "FreeRTOS.h"
#include "semphr.h"
#include "task.h"
#include "board.h"

#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_common.h"
#include "sdcard_fatfs_freertos.h"
#include "sys.h"


/*******************************************************************************
* Definitions
******************************************************************************/

/*! @brief Task stack size. */
#define ACCESSFILE_TASK_STACK_SIZE (1024U)
/*! @brief Task stack priority. */
#define ACCESSFILE_TASK_PRIORITY 9   //(configMAX_PRIORITIES - 3U)

/*! @brief Task stack size. */
#define CARDDETECT_TASK_STACK_SIZE (1024U)
/*! @brief Task stack priority. */
#define CARDDETECT_TASK_PRIORITY 16  //(configMAX_PRIORITIES - 2U)
/*******************************************************************************
* Prototypes
******************************************************************************/
void BOARD_PowerOffSDCARD(void);
void BOARD_PowerOnSDCARD(void);
/*!
* @brief SD card KeyAccessTask.
*
* @param pvParameters Task parameter.
*/
static void KeyAccessTask(void *pvParameters);

/*!
* @brief SD card ConfiguartionTask.
*
* @param pvParameters Task parameter.
*/
static void CfgAccessTask(void *pvParameters);

/*!
* @brief SD card detect task.
*
* @param pvParameters Task parameter.
*/
static void CardDetectTask(void *pvParameters);

/*!
* @brief call back function for SD card detect.
*
* @param isInserted  true,  indicate the card is insert.
*                    false, indicate the card is remove.
* @param userData
*/
static void SDCARD_DetectCallBack(bool isInserted, void *userData);

/*!
* @brief make filesystem.
*/
static status_t DEMO_MakeFileSystem(void);

/*******************************************************************************
* Variables
******************************************************************************/
static FATFS g_fileSystem; /* File system object */
static FIL g_fileObject;   /* File object */
static const uint8_t s_buffer1[] = {'T', 'A', 'S', 'K', '1', '\r', '\n'};
static const uint8_t s_buffer2[] = {'T', 'A', 'S', 'K', '2', '\r', '\n'};


SDK_ALIGN(uint8_t certificate_data[MAX_FILE_SIZE_IN_BYTE], 4);
SDK_ALIGN(uint8_t privatekey_data[MAX_FILE_SIZE_IN_BYTE], 4);
SDK_ALIGN(uint8_t gateway_cfg[MAX_FILE_SIZE_IN_BYTE], 4);

uint32_t certificate_len = 0;

uint32_t privatekey_len = 0;

uint32_t gateway_cfg_len = 0;



/*! @brief SDMMC host detect card configuration */
static const sdmmchost_detect_card_t s_sdCardDetect =
{
#ifndef BOARD_SD_DETECT_TYPE
	.cdType = kSDMMCHOST_DetectCardByGpioCD,
#else
	.cdType = BOARD_SD_DETECT_TYPE,
#endif
	.cdTimeOut_ms = (~0U),
	.cardInserted = SDCARD_DetectCallBack,
	.cardRemoved  = SDCARD_DetectCallBack,
};

/*! @brief SDMMC card power control configuration */
#if defined DEMO_SDCARD_POWER_CTRL_FUNCTION_EXIST
static const sdmmchost_pwr_card_t s_sdCardPwrCtrl =
{
	.powerOn          = BOARD_PowerOnSDCARD,
	.powerOnDelay_ms  = 500U,
	.powerOff         = BOARD_PowerOffSDCARD,
	.powerOffDelay_ms = 0U,
};
#endif

/*! @brief SDMMC card power control configuration */
#if defined DEMO_SDCARD_SWITCH_VOLTAGE_FUNCTION_EXIST
extern const sdmmchost_card_switch_voltage_func_t g_sdCardVoltageSwitch;
#endif
/*! @brief SD card detect flag  */
static volatile bool s_cardInserted     = false;
static volatile bool s_cardInsertStatus = false;
/*! @brief Card semaphore  */
extern SemaphoreHandle_t g_fileAccessSemaphore;
extern SemaphoreHandle_t g_CardDetectSemaphore;
extern SemaphoreHandle_t g_KeyReadSemaphore;
extern SemaphoreHandle_t g_SDCardCfgReadSemaphore;
extern SemaphoreHandle_t g_SDCardCfgEnableSemaphore;
extern SemaphoreHandle_t g_CfgReadSemaphore;
extern SemaphoreHandle_t g_CfgSaveSemaphore;


/*******************************************************************************
* Code
******************************************************************************/
#if defined DEMO_SDCARD_SWITCH_VOLTAGE_FUNCTION_EXIST
const sdmmchost_card_switch_voltage_func_t g_sdCardVoltageSwitch =
{
	.cardSignalLine1V8 = BOARD_USDHC_Switch_VoltageTo1V8,
	.cardSignalLine3V3 = BOARD_USDHC_Switch_VoltageTo3V3,
};
#endif

void BOARD_USDHCClockConfiguration(void)
{
	CLOCK_InitSysPll(&sysPllConfig_BOARD_BootClockRUN);
	/*configure system pll PFD0 fractional divider to 24, output clock is 528MHZ * 18 / 24 = 396 MHZ*/
	CLOCK_InitSysPfd(kCLOCK_Pfd0, 24U);
	/* Configure USDHC clock source and divider */
	CLOCK_SetDiv(kCLOCK_Usdhc1Div, 0U);
	CLOCK_SetMux(kCLOCK_Usdhc1Mux, 1U);
}

void BOARD_PowerOffSDCARD(void)
{
	/*
	Do nothing here.

	SD card will not be detected correctly if the card VDD is power off,
	the reason is caused by card VDD supply to the card detect circuit, this issue is exist on EVK board rev A1 and
	A2.

	If power off function is not implemented after soft reset and prior to SD Host initialization without
	remove/insert card,
	a UHS-I card may not reach its highest speed mode during the second card initialization.
	Application can avoid this issue by toggling the SD_VDD (GPIO) before the SD host initialization.
	*/
}

void BOARD_PowerOnSDCARD(void)
{
	BOARD_USDHC_SDCARD_POWER_CONTROL(true);
}

static void SDCARD_DetectCallBack(bool isInserted, void *userData)
{
	s_cardInsertStatus = isInserted;
//	if(s_cardInsertStatus)
	{
		xSemaphoreGiveFromISR(g_CardDetectSemaphore, NULL);
	}
}

static void CardDetectTask(void *pvParameters)
{
	g_sd.host.base           = SD_HOST_BASEADDR;
	g_sd.host.sourceClock_Hz = SD_HOST_CLK_FREQ;
	g_sd.usrParam.cd         = &s_sdCardDetect;
#if defined DEMO_SDCARD_POWER_CTRL_FUNCTION_EXIST
	g_sd.usrParam.pwr = &s_sdCardPwrCtrl;
#endif
#if defined DEMO_SDCARD_SWITCH_VOLTAGE_FUNCTION_EXIST
	g_sd.usrParam.cardVoltage = &g_sdCardVoltageSwitch;
#endif
	/* SD host init function */
	if (SD_HostInit(&g_sd) == kStatus_Success)
	{
		while (true)
		{
			/* take card detect semaphore */
			if (xSemaphoreTake(g_CardDetectSemaphore, portMAX_DELAY) == pdTRUE)
			{
				if (s_cardInserted != s_cardInsertStatus)
				{
					s_cardInserted = s_cardInsertStatus;

					/* power off card */
					SD_PowerOffCard(g_sd.host.base, g_sd.usrParam.pwr);

					if (s_cardInserted)
					{
						PRINTF("\r\nCard inserted.\r\n");
						/* power on the card */
						SD_PowerOnCard(g_sd.host.base, g_sd.usrParam.pwr);
						/* make file system */
						if (DEMO_MakeFileSystem() != kStatus_Success)
						{
							continue;
						}
						xSemaphoreGive(g_fileAccessSemaphore);
					}
				}

				if (!s_cardInserted)
				{
					PRINTF("\r\nPlease insert a card into board.\r\n");
				}
			}
		}
	}
	else
	{
		PRINTF("\r\nSD host init fail\r\n");
	}

	vTaskSuspend(NULL);
}

/*!
* @brief Main function
*/
void sdcard_fatfs_init(void)
{


	PRINTF("\r\nSDCARD fatfs freertos example.\r\n");
	//yangliang
#if 0
	if (pdPASS != xTaskCreate(KeyAccessTask, "KeyAccessTask", ACCESSFILE_TASK_STACK_SIZE, NULL,
	                          ACCESSFILE_TASK_PRIORITY, NULL))
	{
		;
	}
#endif
	if (pdPASS != xTaskCreate(CfgAccessTask, "CfgAccessTask", ACCESSFILE_TASK_STACK_SIZE, NULL,
	                          ACCESSFILE_TASK_PRIORITY, NULL))
	{
		;
	}

	if (pdPASS !=
	        xTaskCreate(CardDetectTask, "CardDetectTask", CARDDETECT_TASK_STACK_SIZE, NULL, CARDDETECT_TASK_PRIORITY, NULL))
	{
		;
	}

}

static status_t DEMO_MakeFileSystem(void)
{
	FRESULT error;
	const TCHAR driverNumberBuffer[3U] = {SDDISK + '0', ':', '/'};
	BYTE work[FF_MAX_SS];

	if (f_mount(&g_fileSystem, driverNumberBuffer, 0U))
	{
		PRINTF("Mount volume failed.\r\n");
		return kStatus_Fail;
	}

#if (FF_FS_RPATH >= 2U)
	error = f_chdrive((char const *)&driverNumberBuffer[0U]);
	if (error)
	{
		PRINTF("Change drive failed.\r\n");
		return kStatus_Fail;
	}
#endif

#if 0 //FF_USE_MKFS
	PRINTF("\r\nMake file system......The time may be long if the card capacity is big.\r\n");
	if (f_mkfs(driverNumberBuffer, FM_ANY, 0U, work, sizeof work))
	{
		PRINTF("Make file system failed.\r\n");
		return kStatus_Fail;
	}
#endif /* FF_USE_MKFS */

#if 0
	PRINTF("\r\nCreate directory......\r\n");
	error = f_mkdir(_T("/dir_1"));
	if (error)
	{
		if (error == FR_EXIST)
		{
			PRINTF("Directory exists.\r\n");
		}
		else
		{
			PRINTF("Make directory failed.\r\n");
			return kStatus_Fail;
		}
	}
#endif
	return kStatus_Success;
}


static void CfgAccessTask(void *pvParameters)
{
	UINT bytesRead   = 0U;
	FRESULT error;

	while (1)
	{
		/* trying to take the file access semphore */
		if (xSemaphoreTake(g_fileAccessSemaphore, pdMS_TO_TICKS(1000)) == pdTRUE)
		{
			error = f_open(&g_fileObject, _T("GW_cfg.json"), FA_READ);
			if (error)
			{
				if (error == FR_EXIST)
				{
					PRINTF("File exists.\r\n");
				}
				else
				{
					f_close(&g_fileObject);
					xSemaphoreGive(g_fileAccessSemaphore);
					PRINTF("Open file failed.\r\n");
					break;
				}

			}

			/* read append */
			f_lseek(&g_fileObject, g_fileObject.obj.objsize);
			gateway_cfg_len= (uint32_t)f_tell(&g_fileObject);
			if(gateway_cfg_len < MAX_FILE_SIZE_IN_BYTE)
			{
				f_lseek(&g_fileObject, 0);
				error = f_read(&g_fileObject, &gateway_cfg, gateway_cfg_len, &bytesRead);
				if ((error) || (bytesRead == 0))
				{
					PRINTF("Read file failed.\r\n");
					break;
				}

			}
			else
			{
				f_close(&g_fileObject);
				xSemaphoreGive(g_fileAccessSemaphore);
				PRINTF("File too big to read.\r\n");
				break;

			}

			f_close(&g_fileObject);
			xSemaphoreGive(g_SDCardCfgReadSemaphore);
			//xSemaphoreGive(g_fileAccessSemaphore);
			//vTaskDelay(1U);
			//break;
		}
		else
		{
			//PRINTF("TASK2: file access is blocking.\r\n");
		}
	}
	vTaskSuspend(NULL);
}


